/**
 * Jaxson ajax
 *
 * Copyright Remedy Interactive, licensed under Apache version 2 license:
 *
 *  http://www.apache.org/licenses/LICENSE-2.0.html
 *
 * This notice must remain in all java source files.
 */
package org.jaxson.struts.charts.annotations;


import java.io.IOException;
import java.io.Serializable;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Locale;
import java.util.Map;
import java.util.ResourceBundle;

import org.jaxson.util.locale.LocaleUtil;
import org.jfree.data.category.DefaultCategoryDataset;
import org.jfree.data.general.Dataset;
import org.jfree.data.general.DefaultPieDataset;

/**
 * Class which reads in Jaxson annotations used to mark
 * up properties for rendering charts.  The annotations
 * are used to create Datasets for JFreeChart.
 * 
 * @author Joe Maisel
 * 
 */
public class ChartAnnotationReader
{
	private static final Map<Class<? extends Dataset>, DatasetMappingHandler> handlers 
		= new HashMap<Class<? extends Dataset>, DatasetMappingHandler>();

	private Locale locale = Locale.getDefault();
	
	public ChartAnnotationReader ()
	{
		if( handlers.containsKey( DefaultPieDataset.class ) )
			return;
		
		handlers.put( DefaultPieDataset.class, new DatasetMappingHandler()
		{
			@Override
			public void handle( DatasetMapping dm, Dataset ds, Number value, ResourceBundle bundle )
			{
				DefaultPieDataset pds = (DefaultPieDataset)ds;
				pds.setValue( bundle == null?dm.key():bundle.getString( dm.key() ), value );
			}
		});

		handlers.put( DefaultCategoryDataset.class, new DatasetMappingHandler()
		{
			@Override
			public void handle( DatasetMapping dm, Dataset ds, Number value, ResourceBundle bundle )
			{
				DefaultCategoryDataset cds = (DefaultCategoryDataset)ds;
				cds.setValue( value, 
							  bundle == null?dm.rowKey():bundle.getString( dm.rowKey() ), 
							  bundle == null?dm.columnKey():bundle.getString( dm.columnKey() ) );
			}
		});
	}
	
	/**
	 * @return the locale
	 */
	public Locale getLocale()
	{
		return locale;
	}

	/**
	 * @param locale the locale to set
	 */
	public void setLocale( Locale locale )
	{
		this.locale = locale;
	}

	/**
	 * Add a handler to place data into a Dataset.
	 */
	public static void addHandler( Class<? extends Dataset> type, DatasetMappingHandler handler )
	{
		handlers.put( type, handler );
	}
	
	/**
	 * Reads a collection of Serializables, checks them for Jaxson charting annotations
	 * and returns a populated dataset.
	 * 
	 * @throws UnsupportedDatasetException
	 */
	public Dataset read( Collection<? extends Serializable> items, Class<? extends Dataset> dt ) throws UnsupportedDatasetException
	{
		Dataset result = null;

		try
		{
			Iterator<? extends Serializable> i = items.iterator();

			while( i.hasNext() )
			{
				Serializable m = i.next();

				Method[] methods = m.getClass().getMethods();
				for( int j = 0; j < methods.length; j++ )
				{
					Method meth = methods[j];

					if( meth.getName().startsWith( "get" ) )
					{
						Charts c = meth.getAnnotation( Charts.class );

						if( c != null )
						{
							DatasetMapping[] datasets = c.datasets();

							for( int k = 0; k < datasets.length; k++ )
							{
								DatasetMapping mapping = datasets[k];
								ResourceBundle bundle = null;
								
								if( !mapping.bundleName().trim().equals( "" ) )
								{
									bundle = LocaleUtil.getBundle( mapping.bundleName(), locale );
								}
								
								if( mapping.type().equals( dt ) )
								{
									if( result == null )
									{
										result = dt.newInstance();
									}

									Number output = (Number) meth.invoke( m );
									DatasetMappingHandler handler = handlers.get( mapping.type() );

									if( handler == null )
										throw new UnsupportedDatasetException( "No handler for dataset "
												+ mapping.type() );

									handler.handle( mapping, result, output, bundle );
								}
							}
						}
					}
				}
			}
		}
		catch( InstantiationException e )
		{
			e.printStackTrace();
			throw new UnsupportedDatasetException( "Cannot instantiate " + dt + "."
					+ "  Make sure the 'datatype' property specifies a class which can be instantiated with no args." );

		}
		catch( IllegalAccessException e )
		{
			e.printStackTrace();
		}
		catch( IllegalArgumentException e )
		{
			e.printStackTrace();
		}
		catch( InvocationTargetException e )
		{
			e.printStackTrace();
		}
		catch( IOException e )
		{
			e.printStackTrace();
		}

		return result;
	}
}
